using System;
using System.Collections;
using System.Web;
using System.Data;
using System.Web.UI;
using System.ComponentModel;
using System.Web.UI.WebControls;
using Deepcode.Flux.Core.UI.Controls.Helpers;

namespace Deepcode.Flux.Core.UI.Controls
{
	/// <summary>
	/// DataPager provides advanced paging support for deepcode datagrid's.
	/// It facilitates next page/previous page, but also rows per page and 
	/// jumping to specific page numbers. It should be linked to a Flux
	/// datagrid and have data bound to it.
	/// </summary>
	public class DataPager : Deepcode.Flux.Core.UI.Controls.Base.BaseControl, INamingContainer
	{
		public delegate void PagerRepage();
		public delegate void RPPChanged( int NewRPP );
		public event PagerRepage OnRepage;
		public event RPPChanged OnRPPChanged;

		#region Properties
		/// <summary>
		/// Sets the starting record row number for the datapager / grid. If this
		/// is not set, the pager will try to find the starting row in it's viewstate.
		/// If that cannot be found, it will load the startrow from the StartRow URL
		/// parameter. If all else fails, this property will return 1.
		/// </summary>
		public int StartRow
		{
			get
			{
				// See if we have it in the viewstate
				object srVS = ViewState["IStartRow"];
				if( srVS != null )
					return (int) srVS;
				// We don't have it in viewstate - check the startrow parameter				
				string srparameter = this.Page.Request["StartRow"];
				if( (srparameter == null) || (srparameter=="") ) return 1;
				int srValue = 1;
				try{ srValue = Convert.ToInt32( srparameter ); }
				catch{ return 1; }
				
				StartRow = srValue;
				return srValue;
			}
			set
			{
				ViewState["IStartRow"] = value;
			}
		}
		/// <summary>
		/// Specifies the number of rows per page the pager should
		/// manage and bind data to the grid.
		/// </summary>
		public int RowsPerPage
		{
			get
			{
				object o = ViewState["rowsperpage"];
				if( o == null ) return 10;
				return (int) o;
			}
			set
			{
				ViewState["rowsperpage"] = value;
			}
		}
		#endregion
		#region Databinding / Control referencing properties
		/// <summary>
		/// Store the name of the grid control we link up to
		/// </summary>
		public string LinkGrid
		{
			get
			{
				string lg = (string) ViewState["LinkGrid"];
				return lg==null ? String.Empty : lg;
			}
			set
			{
				ViewState["LinkGrid"] = value;
			}
		}
		/// <summary>
		/// Storage property for the datasource object until we bind to it
		/// </summary>
		private object dataSource = null;
		/// <summary>
		/// Get/Set the datasource object
		/// </summary>
		public object DataSource
		{
			get
			{
				return dataSource;
			}
			set
			{
				dataSource = value;
			}
		}
		#endregion
		#region DataBinding
		/// <summary>
		/// Walks the control collection until it finds controlID or runs out of nodes
		/// </summary>
		/// <param name="SearchTree"></param>
		/// <param name="ControlID"></param>
		/// <returns></returns>
		private Control WalkPageTree(ControlCollection SearchTree, string ControlID)
		{
			foreach( Control c in SearchTree )
			{
				// Is this the one we're looking for ?
				if( c.ID == ControlID ) return c;
				// No...look for children
				Control childSearch = WalkPageTree(c.Controls, ControlID);
				if( childSearch != null ) return childSearch;			
			}
			return null;
		}
		/// <summary>
		/// Return the datasource IList
		/// </summary>
		/// <returns></returns>
		private IList GetDataSource()
		{
			if( this.dataSource == null ) return null;
			
			IList resolvedIListDS = this.dataSource as IList;
			if( resolvedIListDS != null ) return resolvedIListDS;

			IListSource resolvedIListSourceDS = this.dataSource as IListSource;
			if( resolvedIListSourceDS != null )
			{
				IList memberList = resolvedIListSourceDS.GetList();
				if( resolvedIListSourceDS.ContainsListCollection == false ) return memberList;
				/*
				 * Here, if it ever becomes necessary to do so, and 
				 * if we're bound to a whole dataset or something, we
				 * can parse against a membername or suchlike
				 */
			}
			return null;
		}
		/// <summary>
		/// Tell our control we've got data to bind to
		/// </summary>
		public override void DataBind()
		{
			// First find the control we're linked to...
			Control c = WalkPageTree( Page.Controls, LinkGrid );
			if( (c==null) || (!(c is DataGrid))) return;
			// Get the grid object
			DataGrid g = (DataGrid) c;
			// Cast out the datasource for the puposes of counting
			IList list = GetDataSource();
			// Work out the rows per page size and the current page we're looking at.
			if( (list != null) && (list.Count>0) )
			{
				// We have records - work out rows per page and so forth
				GridPagingController gpc = GridPagingController.GetPagingController(StartRow, RowsPerPage, list.Count );
				// Update the pager's form
				EnsureChildControls();
				if( gpc.HasNextButton ) _btnNext.Enabled = true;
				else _btnNext.Enabled = false;
				if( gpc.HasPrevButton ) _btnPrev.Enabled = true;
				else _btnPrev.Enabled = false;
				_txtPageNo.Text = "" + gpc.CurrentPage;
				_txtTotalPages.Text = "" + gpc.TotalPages;
				_txtRecordStart.Text = "" + gpc.StartRow;
				_txtRecordEnd.Text = "" + gpc.LastRow;
				_txtTotalRecords.Text = "" + gpc.TotalRecords;
				// Save to viewstate and update the grid information
				gpc.SaveToViewstate( this.ViewState );				
				g.PageSize = gpc.RowsPerPage;
				g.CurrentPageIndex = gpc.CurrentPage-1;
			}
			else
				Visible = false;

			// Tell the grid to bind to it's own data
			g.DataSource = DataSource;
			g.DataBind();
		}
		#endregion
		#region Child Control Declarations
		// This composite control is made up of the following components
		protected System.Web.UI.WebControls.Label		_txtPageNo;
		protected System.Web.UI.WebControls.Label		_txtTotalPages;
		protected System.Web.UI.WebControls.TextBox		_edJumpPage;
		protected System.Web.UI.WebControls.LinkButton	_btnJumpPage;
		protected System.Web.UI.WebControls.Label		_txtRecordStart;
		protected System.Web.UI.WebControls.Label		_txtRecordEnd;
		protected System.Web.UI.WebControls.Label		_txtTotalRecords;
		protected System.Web.UI.WebControls.TextBox		_edRPP;
		protected System.Web.UI.WebControls.LinkButton	_btnSetRPP;
		protected System.Web.UI.WebControls.LinkButton	_btnPrev;
		protected System.Web.UI.WebControls.LinkButton	_btnNext;
		#endregion
		#region Child Control Overrides for creation and rendering		
		/// <summary>
		/// Compose the control from various labels and input boxes
		/// </summary>
		protected override void CreateChildControls()
		{
			Controls.Clear();
			_txtPageNo = new Label();
			_txtPageNo.ID = "txtPageNo";
			
			_txtTotalPages = new Label();
			_txtTotalPages.ID = "txtTotalPages";
			
			_edJumpPage = new TextBox();
			_edJumpPage.ID = "edJumpPage";
			_edJumpPage.Width = 30;
			_edJumpPage.Style.Add("Width", "30px");
			_edJumpPage.Font.Size = 8;

			
			_btnJumpPage = new LinkButton();
			_btnJumpPage.ID = "btnJumpPage";
			_btnJumpPage.Click += new EventHandler(_btnJumpPage_Click);
			_btnJumpPage.Text = "Go";
			
			_txtRecordStart = new Label();
			_txtRecordStart.ID = "txtRecordStart";

			_txtRecordEnd = new Label();
			_txtRecordEnd.ID = "txtRecordEnd";

			_txtTotalRecords = new Label();
			_txtTotalRecords.ID = "txtTotalRecords";

			_edRPP = new TextBox();
			_edRPP.ID = "edRPP";
			_edRPP.Width = 30;
			_edRPP.Style.Add("Width", "30px");
			_edRPP.Font.Size = 8;

			_btnSetRPP = new LinkButton();
			_btnSetRPP.ID = "btnSetRPP";
			_btnSetRPP.Click += new EventHandler(_btnSetRPP_Click);
			_btnSetRPP.Text = "Go";

			_btnPrev = new LinkButton();
			_btnPrev.ID = "btnPrev";
			_btnPrev.Click += new EventHandler(_btnPrev_Click);
			_btnPrev.Text = "<< Previous Page";
			
			_btnNext = new LinkButton();
			_btnNext.ID = "btnNext";
			_btnNext.Click += new EventHandler(_btnNext_Click);
			_btnNext.Text = "Next Page >>";

			Controls.Add(_txtPageNo);
			Controls.Add(_txtTotalPages);
			Controls.Add(_edJumpPage);
			Controls.Add(_btnJumpPage);
			Controls.Add(_txtRecordStart);
			Controls.Add(_txtRecordEnd);
			Controls.Add(_txtTotalRecords);
			Controls.Add(_edRPP);
			Controls.Add(_btnSetRPP);
			Controls.Add(_btnPrev);
			Controls.Add(_btnNext);
		}

		/// <summary> 
		/// Render this control to the output parameter specified.
		/// </summary>
		/// <param name="output"> The HTML writer to write out to </param>
		protected override void Render(HtmlTextWriter output)
		{
			output.Write("<div class='datapager'>");
			output.Write("<table cellspacing=0 cellpadding=5 border=0>");
			output.Write("<tr><td>Page:</td><td><b>");
			_txtPageNo.RenderControl(output);
			output.Write("</b> of <b>");
			_txtTotalPages.RenderControl(output);
			output.Write("</b></td><td>Jump to Page:</td><td>");
			_edJumpPage.RenderControl(output);
			_btnJumpPage.RenderControl(output);
			output.Write("</td></tr><tr><td>Records:</td><td><b>");
			_txtRecordStart.RenderControl(output);
			output.Write("</b> to <b>");
			_txtRecordEnd.RenderControl(output);
			output.Write("</b> of <b>");
			_txtTotalRecords.RenderControl(output);
			output.Write("</b></td><td>Rows per Page:</td><td>");
			_edRPP.RenderControl(output);
			_btnSetRPP.RenderControl(output);
			output.Write("</td></tr><tr><td colspan=2>");
			_btnPrev.RenderControl(output);
			output.Write("</td><td colspan=2 align=right>");
			_btnNext.RenderControl(output);
			output.Write("</td></tr></table>");
			output.Write("</div>");
		}
		#endregion
		#region Button click handlers
		private void _btnJumpPage_Click(object sender, EventArgs e)
		{
			GridPagingController gpc = new GridPagingController();
			gpc.LoadFromViewstate( this.ViewState );
			try
			{
				// Get the new page number from the input box
				int newPageNo = Convert.ToInt32(_edJumpPage.Text);
				// Clear the input box
				_edJumpPage.Text = "";
				// Ensure the page number is in range
				if( newPageNo < 1 ) return;
				if( newPageNo > gpc.TotalPages ) return;
				// Calculate and set the new startrow for the page
				StartRow = ((newPageNo-1) * gpc.RowsPerPage) + 1;
				// Raise the repage event in the host
				if( this.OnRepage != null ) this.OnRepage();
			}
			catch
			{
				// Conversion error or such like
				_edJumpPage.Text = "ERR";
			}
		}

		private void _btnSetRPP_Click(object sender, EventArgs e)
		{
			GridPagingController gpc = new GridPagingController();
			gpc.LoadFromViewstate( this.ViewState );

			try
			{
				// Get the new rows per page
				int newRPP = Convert.ToInt32(_edRPP.Text);
				// Ensure it's valid
				if( newRPP < 1 ) throw new Exception("Invalid");
				// Clear the RPP field
				_edRPP.Text = "";

				this.RowsPerPage = newRPP;
				// Trigger the RPP Changed event so the host can store the
				// RPP value in the users profile or session....
				if( OnRPPChanged != null ) OnRPPChanged(newRPP);
				// Tell the host it needs to repage the data
				if( OnRepage != null ) OnRepage();
			}
			catch
			{
				// Conversion error or such like
				_edRPP.Text = "ERR";
			}

		}

		private void _btnPrev_Click(object sender, EventArgs e)
		{
			GridPagingController gpc = new GridPagingController();
			gpc.LoadFromViewstate( this.ViewState );
			// Set it's new start row
			StartRow = gpc.PrevPageRowStart;
			// Ask host to re-get the data
			if( this.OnRepage != null ) this.OnRepage();
		}

		private void _btnNext_Click(object sender, EventArgs e)
		{
			GridPagingController gpc = new GridPagingController();
			gpc.LoadFromViewstate( this.ViewState );
			// Set it's new start row
			StartRow = gpc.NextPageRowStart;
			// Ask host to re-get the data
			if( this.OnRepage != null ) this.OnRepage();
		}
		#endregion
		#region Initialisation
		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);
			this.LoadClientStyleSheet("DataPager/DataPager.css", "Deepcode.Flux.Core.UI.Controls.DataPager");
		}
		#endregion
	}
}
